package org.fhm.zdte.client.service.proxy;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler;
import io.netty.handler.stream.ChunkedWriteHandler;
import io.netty.handler.timeout.IdleStateHandler;
import org.fhm.substrate.annotation.Component;
import org.fhm.substrate.annotation.Setup;
import org.fhm.zdte.client.config.ProxyConfiguration;
import org.fhm.zdte.client.standard.IRequestProxy;

import java.util.Objects;

/**
 * The websocket proxy of netty.
 *
 * @author 谭波
 * @since 2024/1/5
 */
@Component
public class WebSocketRequestProxy implements IRequestProxy {

    private NioEventLoopGroup boss;

    private NioEventLoopGroup work;

    private ServerBootstrap server;

    private ChannelFuture channel;

    @Setup
    private ProxyConfiguration proxyConfiguration;

    @Setup
    private PageEntranceHandler pageEntranceHandler;

    @Setup
    private ProxyRequestHandler proxyRequestHandler;

    @Override
    public IRequestProxy initial() {
        boss = new NioEventLoopGroup(1);
        work = new NioEventLoopGroup(proxyConfiguration.getWorkCount());
        server = new ServerBootstrap();
        return this;
    }

    @Override
    public void enable() throws InterruptedException {
        server.group(boss, work)
                .channel(NioServerSocketChannel.class)
                .childHandler(new ChannelInitializer<SocketChannel>() {
                    @Override
                    protected void initChannel(SocketChannel ch) {
                        ChannelPipeline pipeline = ch.pipeline();
                        pipeline.addLast(new IdleStateHandler(60 * 60 * 2, 0, 0));
                        pipeline.addLast(new HttpServerCodec());
                        pipeline.addLast(new ChunkedWriteHandler());
                        pipeline.addLast(new HttpObjectAggregator(1024 * 1024));
                        pipeline.addLast(pageEntranceHandler);
                        pipeline.addLast(new WebSocketServerProtocolHandler("/"));
                        pipeline.addLast(proxyRequestHandler);
                    }
                });
        channel = server.bind(proxyConfiguration.getPort()).sync();
    }

    @Override
    public void close() throws InterruptedException {
        if (Objects.nonNull(channel))
            channel.channel().close().sync();
        boss.shutdownGracefully();
        work.shutdownGracefully();
    }
}
